/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2007 OPeNDAP, Inc.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Text;
using NDODS.DAP.Parser;

namespace NDODS.DAP.Exceptions
{
    /// <summary> Holds an exception thrown by OPeNDAP server to a client.
    /// <p/>
    /// Unlike the other OPeNDAP exceptions, this one contains extra methods to
    /// get the various fields sent by the server, and a <code>parse</code> method
    /// to parse the <code>Error</code> sent from the server.
    /// 
    /// <h3>
    /// This class will be changing it's name to NDODS.DAP.DAP2Exception.
    /// I expect that it will be deprecated in the next release.
    /// <h2>You've been warned.</h2> Questions? Ask ndp@opendap.org.
    /// </h3>
    /// 
    /// </summary>
    /// <author>  jehamby
    /// </author>
    /// <version>  $Revision: 15901 $
    /// 
    /// </version>
    [Serializable]
    public class DAP2Exception : Exception
    {
        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Returns the error code.
        /// 
        /// </summary>
        /// <returns> the error code.
        /// </returns>
        /// <summary> Sets the error code.
        /// 
        /// </summary>
        /// <param name="code">the error code.
        /// </param>
        public virtual int ErrorCode
        {
            get { return errorCode; }

            set { errorCode = value; }
        }

        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Returns the error message.
        /// 
        /// </summary>
        /// <returns> the error message.
        /// </returns>
        /// <summary> Sets the error message.
        /// 
        /// </summary>
        /// <param name="msg">the error message.
        /// </param>
        public virtual String ErrorMessage
        {
            get { return errorMessage; }

            set { errorMessage = value; }
        }

        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Returns the program type.
        /// 
        /// </summary>
        /// <returns> the program type.
        /// </returns>
        /// <summary> Sets the program type.
        /// 
        /// </summary>
        /// <param name="type">the program type.
        /// </param>
        public virtual int ProgramType
        {
            get { return programType; }

            set { programType = value; }
        }

        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Returns the program source.
        /// 
        /// </summary>
        /// <returns> the program source.
        /// </returns>
        /// <summary> Sets the program source.
        /// 
        /// </summary>
        /// <param name="source">the program source.
        /// </param>
        public virtual String ProgramSource
        {
            get { return programSource; }

            set { programSource = value; }
        }

        /// <summary> Returns the detail message of this throwable object.
        /// 
        /// </summary>
        /// <returns> the detail message of this throwable object.
        /// </returns>
        public override String Message
        {
            get { return errorMessage; }
        }

        /// <summary> Undefined error.</summary>
        public const int UNDEFINED_ERROR = - 1;

        /// <summary> Unknown error.</summary>
        public const int UNKNOWN_ERROR = 0;

        /// <summary> The file specified by the OPeNDAP URL does not exist.</summary>
        public const int NO_SUCH_FILE = 1;

        /// <summary> The variable specified in the OPeNDAP URL does not exist.</summary>
        public const int NO_SUCH_VARIABLE = 2;

        /// <summary> The expression specified in the OPeNDAP URL is not valid.</summary>
        public const int MALFORMED_EXPR = 3;

        /// <summary> The user has no authorization to Read the OPeNDAP URL.</summary>
        public const int NO_AUTHORIZATION = 4;

        /// <summary> The file specified by the OPeNDAP URL can not be Read.</summary>
        public const int CANNOT_READ_FILE = 5;

        /*
		* Some Error objects may contain programs which can be used to
		* correct the reported error. These programs are run using a public
		* member function of the Error class. If an Error object does not
		* have an associated correction program, the program type is NO_PROGRAM.
		*/

        /// <summary> Undefined program type.</summary>
        public const int UNDEFINED_PROG_TYPE = - 1;

        /// <summary> This Error does not contain a program.</summary>
        public const int NO_PROGRAM = 0;

        /// <summary> This Error contains Java bytecode.</summary>
        public const int JAVA_PROGRAM = 1;

        /// <summary> This Error contains TCL code.</summary>
        public const int TCL_PROGRAM = 2;

        /// <summary> The error code.
        /// 
        /// </summary>
        /// <serial>
        /// </serial>
        private int errorCode;

        /// <summary> The error message.
        /// 
        /// </summary>
        /// <serial>
        /// </serial>
        private String errorMessage;

        /// <summary> The program type.
        /// 
        /// </summary>
        /// <serial>
        /// </serial>
        private int programType;

        /// <summary> The program source.  if programType is TCL_PROGRAM, then this is ASCII
        /// text.  Otherwise, undefined (this will need to become a byte[] array if
        /// the server sends Java bytecodes, for example).
        /// 
        /// </summary>
        /// <serial>
        /// </serial>
        private String programSource;

        /// <summary> Construct an empty <code>DAP2Exception</code>.</summary>
        public DAP2Exception() : base("DAP2Exception")
        {
        }

        /// <summary> Construct a <code>DAP2Exception</code>.</summary>
        public DAP2Exception(String msg) : this()
        {
            errorCode = UNKNOWN_ERROR;
            errorMessage = msg;
        }


        /// <summary> Construct a <code>DAP2Exception</code> with the given message.
        /// 
        /// </summary>
        /// <param name="code">the error core
        /// </param>
        /// <param name="msg"> the error message
        /// </param>
        public DAP2Exception(int code, String msg) : this()
        {
            errorCode = code;
            errorMessage = msg;
        }

        /// <summary> Reads an Error description from the named InputStream.  This
        /// method calls a generated parser to interpret an ASCII representation of an
        /// <code>Error</code>, and regenerate it as a <code>DAP2Exception</code>.
        /// 
        /// </summary>
        /// <param name="is">the InputStream containing the <code>Error</code> to parse.
        /// </param>
        /// <seealso cref="NDODS.DAP.Parser.ErrorParser">
        /// </seealso>
        public void parse(Stream is_Renamed)
        {
            ErrorParser ep = new ErrorParser(is_Renamed);
            try
            {
                ep.ErrorObject(this);
            }
            catch (ParseException e)
            {
                //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
                String msg = e.Message;
                if (msg != null)
                    msg = msg.Replace('\"', '\'');
                errorMessage = "Error parsing server Error object!\n" + msg;
            }
        }

        /// <summary> Print the Error message on the given <code>StreamWriter</code>.
        /// This code can be used by servlets to throw DAP2Exception to client.
        /// 
        /// </summary>
        /// <param name="os">the <code>StreamWriter</code> to use for output.
        /// </param>
        public virtual void print(StreamWriter os)
        {
            //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
            os.WriteLine("Error {");
            //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
            os.WriteLine("    code = " + errorCode + ";");

            // If the error message is wrapped in double quotes, Print it, else,
            // add wrapping double quotes.
            if ((errorMessage != null) && (errorMessage[0] == '"'))
            {
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                os.WriteLine("    message = " + errorMessage + ";");
            }
            else
            {
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                os.WriteLine("    message = \"" + errorMessage + "\";");
            }

            //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
            os.WriteLine("};");
        }

        /// <summary> Print the Error message on the given <code>OutputStream</code>.
        /// 
        /// </summary>
        /// <param name="os">the <code>OutputStream</code> to use for output.
        /// </param>
        /// <seealso cref="DAP2Exception.print(StreamWriter)">
        /// </seealso>
        public void print(Stream os)
        {
            //UPGRADE_WARNING: At least one expression was used more than once in the target code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1181'"
            StreamWriter pw =
                new StreamWriter(
                    new StreamWriter(new StreamWriter(os, Encoding.Default).BaseStream,
                                     new StreamWriter(os, Encoding.Default).Encoding).BaseStream,
                    new StreamWriter(new StreamWriter(os, Encoding.Default).BaseStream,
                                     new StreamWriter(os, Encoding.Default).Encoding).Encoding);
            print(pw);
            pw.Flush();
        }
    }
}